const { Controller } = require('egg')
const fs = require("fs-extra")
const moment = require("moment")

// 获取sign
function getSign(rid, did, tt, crptext) {

  var Ft = Ft || function (s, e) {
    var t = {}
      , o = t.lib = {}
      , n = function () { }
      , d = o.Base = {
        extend: function (r) {
          n.prototype = this;
          var c = new n;
          return r && c.mixIn(r),
            c.hasOwnProperty("init") || (c.init = function () {
              c.$super.init.apply(this, arguments)
            }
            ),
            c.init.prototype = c,
            c.$super = this,
            c
        },
        create: function () {
          var r = this.extend();
          return r.init.apply(r, arguments),
            r
        },
        init: function () { },
        mixIn: function (r) {
          for (var c in r)
            r.hasOwnProperty(c) && (this[c] = r[c]);
          r.hasOwnProperty("toString") && (this.toString = r.toString)
        },
        clone: function () {
          return this.init.prototype.extend(this)
        }
      }
      , _ = o.WordArray = d.extend({
        init: function (r, c) {
          r = this.words = r || [],
            this.sigBytes = c != e ? c : 4 * r.length
        },
        toString: function (r) {
          return (r || g).stringify(this)
        },
        concat: function (r) {
          var c = this.words
            , l = r.words
            , u = this.sigBytes;
          if (r = r.sigBytes,
            this.clamp(),
            u % 4)
            for (var A = 0; A < r; A++)
              c[u + A >>> 2] |= (l[A >>> 2] >>> 24 - 8 * (A % 4) & 255) << 24 - 8 * ((u + A) % 4);
          else if (65535 < l.length)
            for (A = 0; A < r; A += 4)
              c[u + A >>> 2] = l[A >>> 2];
          else
            c.push.apply(c, l);
          return this.sigBytes += r,
            this
        },
        clamp: function () {
          var r = this.words
            , c = this.sigBytes;
          r[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4),
            r.length = s.ceil(c / 4)
        },
        clone: function () {
          var r = d.clone.call(this);
          return r.words = this.words.slice(0),
            r
        },
        random: function (r) {
          for (var c = [], l = 0; l < r; l += 4)
            c.push(4294967296 * s.random() | 0);
          return new _.init(c, r)
        }
      })
      , y = t.enc = {}
      , g = y.Hex = {
        stringify: function (r) {
          var c = r.words;
          r = r.sigBytes;
          for (var l = [], u = 0; u < r; u++) {
            var A = c[u >>> 2] >>> 24 - 8 * (u % 4) & 255;
            l.push((A >>> 4).toString(16)),
              l.push((A & 15).toString(16))
          }
          return l.join("")
        },
        parse: function (r) {
          for (var c = r.length, l = [], u = 0; u < c; u += 2)
            l[u >>> 3] |= parseInt(r.substr(u, 2), 16) << 24 - 4 * (u % 8);
          return new _.init(l, c / 2)
        }
      }
      , i = y.Latin1 = {
        stringify: function (r) {
          var c = r.words;
          r = r.sigBytes;
          for (var l = [], u = 0; u < r; u++)
            l.push(String.fromCharCode(c[u >>> 2] >>> 24 - 8 * (u % 4) & 255));
          return l.join("")
        },
        parse: function (r) {
          for (var c = r.length, l = [], u = 0; u < c; u++)
            l[u >>> 2] |= (r.charCodeAt(u) & 255) << 24 - 8 * (u % 4);
          return new _.init(l, c)
        }
      }
      , C = y.Utf8 = {
        stringify: function (r) {
          try {
            return decodeURIComponent(escape(i.stringify(r)))
          } catch (c) {
            throw Error("Malformed UTF-8 data")
          }
        },
        parse: function (r) {
          return i.parse(unescape(encodeURIComponent(r)))
        }
      }
      , m = o.BufferedBlockAlgorithm = d.extend({
        reset: function () {
          this._data = new _.init,
            this._nDataBytes = 0
        },
        _append: function (r) {
          typeof r == "string" && (r = C.parse(r)),
            this._data.concat(r),
            this._nDataBytes += r.sigBytes
        },
        _process: function (r) {
          var c = this._data
            , l = c.words
            , u = c.sigBytes
            , A = this.blockSize
            , L = u / (4 * A)
            , L = r ? s.ceil(L) : s.max((L | 0) - this._minBufferSize, 0);
          if (r = L * A,
            u = s.min(4 * r, u),
            r) {
            for (var x = 0; x < r; x += A)
              this._doProcessBlock(l, x);
            x = l.splice(0, r),
              c.sigBytes -= u
          }
          return new _.init(x, u)
        },
        clone: function () {
          var r = d.clone.call(this);
          return r._data = this._data.clone(),
            r
        },
        _minBufferSize: 0
      });
    o.Hasher = m.extend({
      cfg: d.extend(),
      init: function (r) {
        this.cfg = this.cfg.extend(r),
          this.reset()
      },
      reset: function () {
        m.reset.call(this),
          this._doReset()
      },
      update: function (r) {
        return this._append(r),
          this._process(),
          this
      },
      finalize: function (r) {
        return r && this._append(r),
          this._doFinalize()
      },
      blockSize: 16,
      _createHelper: function (r) {
        return function (c, l) {
          return new r.init(l).finalize(c)
        }
      },
      _createHmacHelper: function (r) {
        return function (c, l) {
          return new a.HMAC.init(r, l).finalize(c)
        }
      }
    });
    var a = t.algo = {};
    return t
  }(Math);
  (function (s) {
    function e(m, a, r, c, l, u, A) {
      return m = m + (a & r | ~a & c) + l + A,
        (m << u | m >>> 32 - u) + a
    }
    function t(m, a, r, c, l, u, A) {
      return m = m + (a & c | r & ~c) + l + A,
        (m << u | m >>> 32 - u) + a
    }
    function o(m, a, r, c, l, u, A) {
      return m = m + (a ^ r ^ c) + l + A,
        (m << u | m >>> 32 - u) + a
    }
    function n(m, a, r, c, l, u, A) {
      return m = m + (r ^ (a | ~c)) + l + A,
        (m << u | m >>> 32 - u) + a
    }
    for (var d = Ft, g = d.lib, _ = g.WordArray, y = g.Hasher, g = d.algo, i = [], C = 0; 64 > C; C++)
      i[C] = 4294967296 * s.abs(s.sin(C + 1)) | 0;
    g = g.MD5 = y.extend({
      _doReset: function () {
        this._hash = new _.init([1732584193, 4023233417, 2562383102, 271733878])
      },
      _doProcessBlock: function (m, a) {
        for (var r = 0; 16 > r; r++) {
          var c = a + r
            , l = m[c];
          m[c] = (l << 8 | l >>> 24) & 16711935 | (l << 24 | l >>> 8) & 4278255360
        }
        var r = this._hash.words
          , c = m[a + 0]
          , l = m[a + 1]
          , u = m[a + 2]
          , A = m[a + 3]
          , L = m[a + 4]
          , x = m[a + 5]
          , I = m[a + 6]
          , E = m[a + 7]
          , P = m[a + 8]
          , T = m[a + 9]
          , j = m[a + 10]
          , V = m[a + 11]
          , G = m[a + 12]
          , H = m[a + 13]
          , X = m[a + 14]
          , D = m[a + 15]
          , h = r[0]
          , v = r[1]
          , f = r[2]
          , p = r[3]
          , h = e(h, v, f, p, c, 7, i[0])
          , p = e(p, h, v, f, l, 12, i[1])
          , f = e(f, p, h, v, u, 17, i[2])
          , v = e(v, f, p, h, A, 22, i[3])
          , h = e(h, v, f, p, L, 7, i[4])
          , p = e(p, h, v, f, x, 12, i[5])
          , f = e(f, p, h, v, I, 17, i[6])
          , v = e(v, f, p, h, E, 22, i[7])
          , h = e(h, v, f, p, P, 7, i[8])
          , p = e(p, h, v, f, T, 12, i[9])
          , f = e(f, p, h, v, j, 17, i[10])
          , v = e(v, f, p, h, V, 22, i[11])
          , h = e(h, v, f, p, G, 7, i[12])
          , p = e(p, h, v, f, H, 12, i[13])
          , f = e(f, p, h, v, X, 17, i[14])
          , v = e(v, f, p, h, D, 22, i[15])
          , h = t(h, v, f, p, l, 5, i[16])
          , p = t(p, h, v, f, I, 9, i[17])
          , f = t(f, p, h, v, V, 14, i[18])
          , v = t(v, f, p, h, c, 20, i[19])
          , h = t(h, v, f, p, x, 5, i[20])
          , p = t(p, h, v, f, j, 9, i[21])
          , f = t(f, p, h, v, D, 14, i[22])
          , v = t(v, f, p, h, L, 20, i[23])
          , h = t(h, v, f, p, T, 5, i[24])
          , p = t(p, h, v, f, X, 9, i[25])
          , f = t(f, p, h, v, A, 14, i[26])
          , v = t(v, f, p, h, P, 20, i[27])
          , h = t(h, v, f, p, H, 5, i[28])
          , p = t(p, h, v, f, u, 9, i[29])
          , f = t(f, p, h, v, E, 14, i[30])
          , v = t(v, f, p, h, G, 20, i[31])
          , h = o(h, v, f, p, x, 4, i[32])
          , p = o(p, h, v, f, P, 11, i[33])
          , f = o(f, p, h, v, V, 16, i[34])
          , v = o(v, f, p, h, X, 23, i[35])
          , h = o(h, v, f, p, l, 4, i[36])
          , p = o(p, h, v, f, L, 11, i[37])
          , f = o(f, p, h, v, E, 16, i[38])
          , v = o(v, f, p, h, j, 23, i[39])
          , h = o(h, v, f, p, H, 4, i[40])
          , p = o(p, h, v, f, c, 11, i[41])
          , f = o(f, p, h, v, A, 16, i[42])
          , v = o(v, f, p, h, I, 23, i[43])
          , h = o(h, v, f, p, T, 4, i[44])
          , p = o(p, h, v, f, G, 11, i[45])
          , f = o(f, p, h, v, D, 16, i[46])
          , v = o(v, f, p, h, u, 23, i[47])
          , h = n(h, v, f, p, c, 6, i[48])
          , p = n(p, h, v, f, E, 10, i[49])
          , f = n(f, p, h, v, X, 15, i[50])
          , v = n(v, f, p, h, x, 21, i[51])
          , h = n(h, v, f, p, G, 6, i[52])
          , p = n(p, h, v, f, A, 10, i[53])
          , f = n(f, p, h, v, j, 15, i[54])
          , v = n(v, f, p, h, l, 21, i[55])
          , h = n(h, v, f, p, P, 6, i[56])
          , p = n(p, h, v, f, D, 10, i[57])
          , f = n(f, p, h, v, I, 15, i[58])
          , v = n(v, f, p, h, H, 21, i[59])
          , h = n(h, v, f, p, L, 6, i[60])
          , p = n(p, h, v, f, V, 10, i[61])
          , f = n(f, p, h, v, u, 15, i[62])
          , v = n(v, f, p, h, T, 21, i[63]);
        r[0] = r[0] + h | 0,
          r[1] = r[1] + v | 0,
          r[2] = r[2] + f | 0,
          r[3] = r[3] + p | 0
      },
      _doFinalize: function () {
        var m = this._data
          , a = m.words
          , r = 8 * this._nDataBytes
          , c = 8 * m.sigBytes;
        a[c >>> 5] |= 128 << 24 - c % 32;
        var l = s.floor(r / 4294967296);
        for (a[(c + 64 >>> 9 << 4) + 15] = (l << 8 | l >>> 24) & 16711935 | (l << 24 | l >>> 8) & 4278255360,
          a[(c + 64 >>> 9 << 4) + 14] = (r << 8 | r >>> 24) & 16711935 | (r << 24 | r >>> 8) & 4278255360,
          m.sigBytes = 4 * (a.length + 1),
          this._process(),
          m = this._hash,
          a = m.words,
          r = 0; 4 > r; r++)
          c = a[r],
            a[r] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) & 4278255360;
        return m
      },
      clone: function () {
        var m = y.clone.call(this);
        return m._hash = this._hash.clone(),
          m
      }
    }),
      d.MD5 = y._createHelper(g),
      d.HmacMD5 = y._createHmacHelper(g)
  }
  )(Math);
  (CryptoJS = Ft);

  const obj = JSON.parse(`{"crptext":"${crptext}"}`)
  eval(obj.crptext)

  const str = ub98484234(rid, did, tt)
  // 正则提取  v=250120241123&did=f90aff7b48088d6bcd291128000417p1&tt=1732426272&sign=2b857514635f91990730fa601d412252 中的 2b857514635f91990730fa601d412252
  const reg = /sign=([^&]+)/
  const result = reg.exec(str)[1]

  return result
}

class DouyuController extends Controller {

  async index() {
    const { ctx } = this

    const debug = ctx.query.debug

    // 斗鱼房间id
    const roomid = ctx.params.id

    // 时间戳
    let tt = Math.floor(new Date().getTime() / 1e3)

    // 视频清晰度
    let rate = 2

    try {

      const [result0, result1] = await Promise.all([
        this.getRoomid(roomid),
        this.getVer()
      ])

      // 获取房间id 、参数v、crptext加密函数
      const [rid, v, crptext] = result0
      if (!rid || !v) throw new Error('房间(或参数v)不存在')

      // 获取ver 以及VITE_CLIENT_ID
      const [ver, VITE_CLIENT_ID] = result1
      if (!ver || !VITE_CLIENT_ID) throw new Error('ver(或VITE_CLIENT_ID)不存在')

      // 通过 VITE_CLIENT_ID获取 did
      const did = await this.getDid(VITE_CLIENT_ID)
      if (!did) throw new Error('did不存在')

      // 获取sign
      const sign = getSign(rid, did, tt, crptext)
      if (!sign) throw new Error('sign不存在')

      // 构造payload
      const payload = {
        v: v.toString(),
        did: did.toString(),
        tt: tt.toString(),
        sign: sign.toString(),
        ver: ver.toString(),
        rid: rid.toString(),
        rate: rate.toString()
      }

      // 发送请求
      const result = await ctx.curl("https://m.douyu.com/hgapi/livenc/room/getStreamUrl", {
        method: 'POST',
        data: payload,
        dataType: 'json'
      })

      // 获取真实地址
      const realUrl = result?.data?.data?.url

      if (debug !== undefined || !realUrl) {
        ctx.body = {
          payload,
          result: result.data.data.url
        }
      }
      else ctx.redirect(realUrl)

    } catch (e) {
      console.log(e)
      ctx.status = 400
      ctx.body = { "result": false, "message": e.message }
    }

  }

  // 获取房间号
  async getRoomid(rid) {
    const { ctx } = this

    const result = []

    const url = `https://m.douyu.com/${rid}`
    const res = await ctx.curl(url, { dataType: "text" })

    // 正则查找 rid":(\d{1,8}),"vipId
    let regRes = /rid":(\d{1,8}),"vipId/.exec(res.data)
    result[0] = regRes?.[1]

    // 正则查找   var vdwdae325w_64we = "250120241123"; 中的250120241123
    regRes = /var vdwdae325w_64we = "(\d+)";/.exec(res.data)
    result[1] = regRes?.[1]

    // 正则查找  加密函数
    regRes = /"crptext":"(.+)","abortReason"/.exec(res.data)
    result[2] = regRes?.[1]

    return result
  }

  // 获取VITE_CLIENT_ID
  async getVer() {

    const { ctx } = this

    const result = []

    const url = `https://shark2.douyucdn.cn/front-publish/ord-m-douyu-front-nodejs-master/client/assets/chunks/chunk-BjOAxfZb.js`
    const res = await ctx.curl(url, { dataType: "text" })

    // 正则查找 const si="22011191", 中的 22011191
    let regRes = /const si="(\d+)",/.exec(res.data)
    result[0] = regRes?.[1]

    // 正则查找 VITE_CLIENT_ID:"86", 中的86
    regRes = /VITE_CLIENT_ID:"(\d+)",/.exec(res.data)
    result[1] = regRes?.[1]

    return result
  }

  // 获取did
  async getDid(client_id) {

    const { ctx } = this

    const url = `https://passport.douyu.com/lapi/did/api/get?client_id=${client_id}&callback=__jp0`

    const res = await ctx.curl(url, {
      method: 'GET',
      headers: {
        referer: "https://m.douyu.com/"
      },
      dataType: "text"
    })

    // 正则查找 "did":"80f845671288486bcd096397000417p1" 中的 80f845671288486bcd096397000417p1
    let regRes = /"did":"(\w+)"/.exec(res.data)
    let result = regRes?.[1]

    return result
  }

}

module.exports = DouyuController
